package com.ljk.util.zip;

import com.ljk.util.UtilException;
//import org.apache.tools.zip.ZipFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;


/**
 * 压缩工具类
 *
 * @author ljk
 * @since 2022/1/11 10:30
 */
//@SuppressWarnings("ResultOfMethodCallIgnored")
public class ZipUtil {

    private static final int BUFFER = 1024;

    /**
     * 解压zip文件并读取其中的txt然后解析成实体类,适用于小数据量,要解析成的类需要实现UnZipParser
     *
     * @param downFilePath zip文件全路径
     * @param clz          要解析的类的class
     * @author ljk
     * @since 2022/3/16 15:04
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> unZipParse(String downFilePath, Class<? extends ZipParser> clz) {
        ArrayList<T> list = new ArrayList<>();
        try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(downFilePath))) {
            ZipEntry zipEntry;
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                if (zipEntry.isDirectory()) {
                    throw new UtilException("压缩包内的文件不是TXT格式,请检查");
                }
                BufferedReader br = new BufferedReader(new InputStreamReader(new BufferedInputStream(zipInputStream)));
                String line;
                while ((line = br.readLine()) != null) {
                    ZipParser zip = clz.newInstance();
                    zip.fromString(line);
                    list.add((T) zip);
                }
            }
        } catch (FileNotFoundException e) {
            throw new UtilException(String.format("未找到[%s],无法解析此压缩包!", downFilePath));
        } catch (IOException e) {
//            String str = null;
//            if ("archive is not a ZIP archive".equals(e.getMessage())) {
//                //当提供的文件有问题时,删除下载的文件
//                try {
//                    File file = new File(downFilePath);
//                    try (FileInputStream fis = new FileInputStream(file);) {
//                        byte[] bytes = new byte[1024];
//                        fis.read(bytes);
//                        str = new String(bytes).trim();
//                    } catch (Exception ignore) {
//                    }
//                    if (file.exists() && !file.delete()) {
//                        file.deleteOnExit();
//                    }
//                } catch (Exception ignored) {
//                    throw new UtilException(String.format("文件不正确,系统尝试删除文件但是发生错误,请联系管理,文件路径[%s],错误信息[%s]", downFilePath, e.getMessage()), e);
//                }
//            }
//            throw new UtilException(String.format("解析ZIP压缩文件失败,错误信息[%s],尝试解析文件包的部分内容为:[%s]", e.getMessage(), str), e);
            throw new UtilException("读取文件流异常:" + e.getMessage());
        } catch (InstantiationException | IllegalAccessException e) {
            throw new UtilException("构造实体类异常:" + e.getMessage());
        }
        return list;
    }

    /**
     * 将list写成txt并压入zip文件夹内
     *
     * @param zipFloder zip存放文件夹
     * @param fileName  zip以及txt文件名
     * @param details   要写入文件的list(需要写入文件的类需要实现ZipParser中的parseString接口)
     * @return zip文件全路径
     * @author ljk
     * @since 2022/3/16 15:02
     */
    public static String zipParse(String zipFloder, String fileName, List<? extends ZipParser> details) {
        File file = new File(zipFloder);
        if (!file.exists()) {
            file.mkdirs();
        }
        String zipFile = zipFloder + fileName + ".zip";
        try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(Paths.get(zipFile)))) {
            //增加一个文本文件
            ZipEntry zipEntry = new ZipEntry(fileName + ".txt");
            //创建文件并将指针指向此文件
            zos.putNextEntry(zipEntry);
            //将值写入当前文件
            for (ZipParser detail : details) {
                byte[] bytes = detail.parseString().getBytes();
                zos.write(bytes);
            }
            zos.flush();
        } catch (IOException e) {
            throw new UtilException(String.format("创建zip文件失败,异常原因:%s", e.getMessage()), e);
        }
        return zipFile;
    }


    /**
     * 解压Zip文件
     *
     * @param path 文件目录
     */
    public static void unZip(String path, String savepath) {
        if (savepath == null || savepath.trim().isEmpty()) {
            // 保存解压文件目录
            savepath = path.substring(0, path.lastIndexOf(".")) + File.separator;
        }
        // 创建保存目录
        File homeFile = new File(savepath).getParentFile();
        homeFile.mkdir();
        savepath = homeFile.getPath() + File.separator;
        try (ZipFile zipFile = new ZipFile(path)) {
            // 解决中文乱码问题
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                saveFile(zipFile, entry, savepath);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }


    /**
     * 保存文件
     *
     * @param zipFile  压缩文件
     * @param entry    压缩文件对
     * @param savepath 保存路径
     * @author ljk
     * @since 2021/10/12 14:40
     */
    private static void saveFile(ZipFile zipFile, ZipEntry entry, String savepath) throws IOException {
        String filename = savepath + entry.getName();
        File file = new File(filename);
        // 如果是文件夹先创建
        if (entry.isDirectory()) {
            file.mkdirs();
            return;
        }
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        try (InputStream is = zipFile.getInputStream(entry);
             BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(file.toPath()), BUFFER)) {
            // 创建文件
            file.createNewFile();
            byte[] buf = new byte[BUFFER];
            int count;
            BufferedReader br = new BufferedReader(new InputStreamReader(new BufferedInputStream(is)));
            System.out.println(br.readLine());
            while ((count = is.read(buf)) > -1) {
                bos.write(buf, 0, count);
            }
            bos.flush();
        }
    }

    /**
     * 压缩文件（文件夹）
     *
     * @param path   目标文件流
     * @param format zip 格式 | rar 格式
     */
    public static String zip(File path, String format) {
        String generatePath;
        if (path.isDirectory()) {
            generatePath = !path.getParent().endsWith("/") ? path.getParent() + File.separator + path.getName() + "." + format : path.getParent() + path.getName() + "." + format;
        } else {
            generatePath = !path.getParent().endsWith("/") ? path.getParent() + File.separator : path.getParent();
            generatePath += path.getName().substring(0, path.getName().lastIndexOf(".")) + "." + format;
        }
        try (
                // 输出流
                FileOutputStream outputStream = new FileOutputStream(generatePath);
                // 压缩输出流
                ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(outputStream))) {
            zipSon(out, path, "");
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return generatePath;
    }

    /**
     * 递归压缩文件
     *
     * @param output    ZipOutputStream 对象流
     * @param file      压缩的目标文件流
     * @param childPath 条目目录
     */
    private static void zipSon(ZipOutputStream output, File file, String childPath) throws IOException {
        FileInputStream input = null;
        try {
            // 文件为目录
            if (file.isDirectory()) {
                // 得到当前目录里面的文件列表
                File[] list = file.listFiles();
                childPath = childPath + (childPath.length() == 0 ? "" : "/") + file.getName();
                // 循环递归压缩每个文件
                if (list != null) {
                    for (File f : list) {
                        zipSon(output, f, childPath);
                    }
                }
            } else {
                // 压缩文件
                childPath = (childPath.length() == 0 ? "" : childPath + "/") + file.getName();
                output.putNextEntry(new ZipEntry(childPath));
                input = new FileInputStream(file);
                int readLen;
                byte[] buffer = new byte[1024 * 8];
                while ((readLen = input.read(buffer, 0, BUFFER)) != -1) {
                    output.write(buffer, 0, readLen);
                }
            }
        } finally {
            // 关闭流
            if (input != null) {
                try {
                    input.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

}
